"""
Structured logging implementation following SOLID principles.
"""

import logging
import sys
from typing import Any, Dict, Optional
from datetime import datetime
import structlog
from ..config.settings import get_settings


class Logger:
    """Structured logger wrapper."""
    
    def __init__(self, name: str, level: str = "INFO"):
        self.name = name
        self.level = level
        self._setup_logging()
        self.logger = structlog.get_logger(name)
    
    def _setup_logging(self):
        """Setup structured logging configuration."""
        logging.basicConfig(
            format="%(message)s",
            stream=sys.stdout,
            level=getattr(logging, self.level.upper())
        )
        
        structlog.configure(
            processors=[
                structlog.stdlib.filter_by_level,
                structlog.stdlib.add_logger_name,
                structlog.stdlib.add_log_level,
                structlog.stdlib.PositionalArgumentsFormatter(),
                structlog.processors.TimeStamper(fmt="iso"),
                structlog.processors.StackInfoRenderer(),
                structlog.processors.format_exc_info,
                structlog.processors.UnicodeDecoder(),
                structlog.processors.JSONRenderer()
            ],
            context_class=dict,
            logger_factory=structlog.stdlib.LoggerFactory(),
            wrapper_class=structlog.stdlib.BoundLogger,
            cache_logger_on_first_use=True,
        )
    
    def info(self, message: str, **kwargs):
        """Log info message."""
        self.logger.info(message, **kwargs)
    
    def debug(self, message: str, **kwargs):
        """Log debug message."""
        self.logger.debug(message, **kwargs)
    
    def warning(self, message: str, **kwargs):
        """Log warning message."""
        self.logger.warning(message, **kwargs)
    
    def error(self, message: str, **kwargs):
        """Log error message."""
        self.logger.error(message, **kwargs)
    
    def critical(self, message: str, **kwargs):
        """Log critical message."""
        self.logger.critical(message, **kwargs)
    
    def exception(self, message: str, **kwargs):
        """Log exception with traceback."""
        self.logger.exception(message, **kwargs)
    
    def bind(self, **kwargs) -> 'Logger':
        """Bind context to logger."""
        bound_logger = self.logger.bind(**kwargs)
        new_logger = Logger(self.name, self.level)
        new_logger.logger = bound_logger
        return new_logger
    
    def log_request(
        self,
        method: str,
        url: str,
        status_code: int,
        duration: float,
        user_id: Optional[str] = None,
        **kwargs
    ):
        """Log HTTP request."""
        self.logger.info(
            "HTTP Request",
            method=method,
            url=url,
            status_code=status_code,
            duration_ms=round(duration * 1000, 2),
            user_id=user_id,
            timestamp=datetime.utcnow().isoformat(),
            **kwargs
        )
    
    def log_database_query(
        self,
        query: str,
        duration: float,
        table: Optional[str] = None,
        **kwargs
    ):
        """Log database query."""
        self.logger.debug(
            "Database Query",
            query=query,
            duration_ms=round(duration * 1000, 2),
            table=table,
            timestamp=datetime.utcnow().isoformat(),
            **kwargs
        )
    
    def log_cache_operation(
        self,
        operation: str,
        key: str,
        hit: bool,
        duration: Optional[float] = None,
        **kwargs
    ):
        """Log cache operation."""
        self.logger.debug(
            "Cache Operation",
            operation=operation,
            key=key,
            hit=hit,
            duration_ms=round(duration * 1000, 2) if duration else None,
            timestamp=datetime.utcnow().isoformat(),
            **kwargs
        )


def get_logger(name: str) -> Logger:
    """Get logger instance."""
    settings = get_settings()
    return Logger(name, settings.log_level)